跳到主要内容

02. Fields

Field 函数用于自定义字段,为其添加元数据

默认值 default

from pydantic import BaseModel, Field


class User(BaseModel):
name: str = Field(default='John Doe')


user = User()
print(user)
#> name='John Doe'

还可以使用 default_factory 定义一个可变的默认值

from uuid import uuid4

from pydantic import BaseModel, Field


class User(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)

If you use typing.Optional, it doesn't mean that the field has a default value of None!

Annotated 合用

Field 可以写进 Annotated 哦(不过有啥用嘛)

from uuid import uuid4

from typing_extensions import Annotated

from pydantic import BaseModel, Field


class User(BaseModel):
id: Annotated[str, Field(default_factory=lambda: uuid4().hex)]

default_factory 只能写在 Annotated 里面,而 default 无法写在里面,只能直接在外面通过赋值的形式写

字段别名 alias

定义一个 alias 有三种方法:

  • Field(..., alias='foo')
  • Field(..., validation_alias='foo')
  • Field(..., serialization_alias='foo')

alias 在验证序列化时都被用到,除非用第二、三种方法分别开来

from pydantic import BaseModel, Field


class User(BaseModel):
name: str = Field(..., alias='username')


user = User(username='johndoe')
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True))
#> {'username': 'johndoe'}

by_alias=True 一定要显式地写出来!默认是 False

If you want to use an alias only for validation, you can use the validation_alias parameter:

from pydantic import BaseModel, Field


class User(BaseModel):
name: str = Field(..., validation_alias='username')


user = User(username='johndoe')
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True))
#> {'name': 'johndoe'}

If you only want to define an alias for serialization, you can use the serialization_alias parameter:

from pydantic import BaseModel, Field


class User(BaseModel):
name: str = Field(..., serialization_alias='username')


user = User(name='johndoe')
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True))
#> {'username': 'johndoe'}

数字条件约束

可以用以下参数来限制数字型字段的值:

  • gt - greater than
  • lt - less than
  • ge - greater than or equal to
  • le - less than or equal to
  • multiple_of - a multiple of the given number 给定数字的倍数
  • allow_inf_nan - allow 'inf''-inf''nan' values
from pydantic import BaseModel, Field


class Foo(BaseModel):
positive: int = Field(gt=0)
non_negative: int = Field(ge=0)
negative: int = Field(lt=0)
non_positive: int = Field(le=0)
even: int = Field(multiple_of=2)
love_for_pydantic: float = Field(allow_inf_nan=True)


foo = Foo(
positive=1,
non_negative=0,
negative=-1,
non_positive=0,
even=2,
love_for_pydantic=float('inf'),
)
print(foo)
"""
positive=1 non_negative=0 negative=-1 non_positive=0 even=2 love_for_pydantic=inf
"""

对那些混合类型来说,使用数字约束可能会出错。使用 Annotated 来避免这种情况

from typing import Optional

from typing_extensions import Annotated

from pydantic import BaseModel, Field


class Foo(BaseModel):
positive: Optional[Annotated[int, Field(gt=0)]]
# Can error in some cases, not recommended:
non_negative: Optional[int] = Field(ge=0)

字符串条件约束

NameTypeDescription
strip_whitespacebool | None是否删除前后空白
to_upperbool | None是否转换为大写
to_lowerbool | None是否转换为小写
strictbool | None是否使用 strict mode 去验证字符串
min_lengthint | None|The minimum length of the string.
max_lengthint | NoneThe maximum length of the string.
patternstr | Pattern[str] | None正则 pattern

example

from pydantic import BaseModel, Field


class Foo(BaseModel):
short: str = Field(min_length=3)
long: str = Field(max_length=10)
regex: str = Field(pattern=r'^\d*$')


foo = Foo(short='foo', long='foobarbaz', regex='123')
print(foo)
#> short='foo' long='foobarbaz' regex='123'

十进制数字条件约束

  • max_digits: 允许的最大数字个数,不包括小数点前后的零。
  • decimal_places: 允许的最大小数位数。不包括小数后面的零。
from decimal import Decimal

from pydantic import BaseModel, Field


class Foo(BaseModel):
precise: Decimal = Field(max_digits=5, decimal_places=2)


foo = Foo(precise=Decimal('123.45'))
print(foo)
#> precise=Decimal('123.45')

dataclass 的条件约束

  • init: 字段是否一定要在 dataclass 的 __init__ 函数中提前定义
  • init_var: Whether the field should be seen as an init-only field in the dataclass.
  • kw_only: 字段是否一定要以关键字参数的形式传参
from pydantic import BaseModel, Field
from pydantic.dataclasses import dataclass


@dataclass
class Foo:
bar: str
baz: str = Field(init_var=True)
qux: str = Field(kw_only=True)


class Model(BaseModel):
foo: Foo


model = Model(foo=Foo('bar', baz='baz', qux='qux'))
print(model.model_dump())
#> {'foo': {'bar': 'bar', 'qux': 'qux'}}

验证默认值

validate_default 决定字段的默认值是否需要被验证,默认为 False

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
age: int = Field(default='twelve', validate_default=True)


try:
user = User()
except ValidationError as e:
print(e)
"""
1 validation error for User
age
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twelve', input_type=str]
"""

字段展示 repr

repr 决定字段是否出现在  string representation 中,默认为 True

from pydantic import BaseModel, Field


class User(BaseModel):
    name: str = Field(repr=True)
    age: int = Field(repr=False)

user = User(name="John", age=42)

print(user)
print(user.model_dump())
#> name='John'
#> {'name': 'John', 'age': 42}

只是没有在 __repr__ 的时候展示出来啦。。序列化的时候还是会有的,得用 exclude

鉴别器 Discriminator

Discriminator 用于校验联合类型中的共有字段。It takes either the name of a field or a Discriminator instance.

from typing import Literal

from pydantic import BaseModel, Field


class Cat(BaseModel):
pet_type: Literal['cat']
age: int


class Dog(BaseModel):
pet_type: Literal['dog']
age: int


class Model(BaseModel):
pet: Cat | Dog = Field(discriminator='pet_type')


print(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}}))
#> pet=Cat(pet_type='cat', age=12)

使用 Discriminator instance:

from typing import Literal, Union

from typing_extensions import Annotated

from pydantic import BaseModel, Discriminator, Field, Tag


class Cat(BaseModel):
pet_type: Literal['cat']
age: int


class Dog(BaseModel):
pet_kind: Literal['dog']
age: int


def pet_discriminator(v):
if isinstance(v, dict):
return v.get('pet_type', v.get('pet_kind'))
return getattr(v, 'pet_type', getattr(v, 'pet_kind', None))


class Model(BaseModel):
pet: Union[Annotated[Cat, Tag('cat')], Annotated[Dog, Tag('dog')]] = Field(
discriminator=Discriminator(pet_discriminator)
)


print(repr(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}})))
#> Model(pet=Cat(pet_type='cat', age=12))

print(repr(Model.model_validate({'pet': {'pet_kind': 'dog', 'age': 12}})))
#> Model(pet=Dog(pet_kind='dog', age=12))

严格模式 Strict Mode

strict 是否开启严格模式校验,默认为 True

from pydantic import BaseModel, Field


class User(BaseModel):
name: str = Field(strict=True)
age: int = Field(strict=False)


user = User(name='John', age='42') # The `age` field is not validated in the strict mode. Therefore, it can be assigned a string.
print(user)
#> name='John' age=42

不变性 Immutability

 frozen 模仿了 frozen dataclass 的行为,避免在初始化后 model 的值被修改

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
name: str = Field(frozen=True)
age: int


user = User(name='John', age=42)

try:
user.name = 'Jane'
except ValidationError as e:
print(e)
"""
1 validation error for User
name
Field is frozen [type=frozen_field, input_value='Jane', input_type=str]
"""

排除 Exclude

exclude 能让 model 在导出(export,即序列化)时忽略字段

from pydantic import BaseModel, Field


class User(BaseModel):
name: str
age: int = Field(exclude=True)


user = User(name='John', age=42)
print(user.model_dump())
#> {'name': 'John'}

废弃的字段 Deprecated

deprecated 标记过期字段,这会导致两种结果:

  • a runtime deprecation warning emitted when accessing the field.
  • "deprecated": true being set in the generated JSON schema.

deprecated 可以是:

  • 一个字符串,标明废弃信息
  • An instance of the warnings.deprecated decorator (or the typing_extensions backport)
  • 一个布尔值
from typing_extensions import Annotated

from pydantic import BaseModel, Field


class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated='This is deprecated')]


print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}
from typing_extensions import Annotated, deprecated

from pydantic import BaseModel, Field


class Model(BaseModel):
deprecated_field: Annotated[int, deprecated('This is deprecated')]

# Or explicitly using `Field`:
alt_form: Annotated[int, Field(deprecated=deprecated('This is deprecated'))]
from typing_extensions import Annotated

from pydantic import BaseModel, Field


class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated=True)]


print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}

自定义 JSON Schema

Some field parameters are used exclusively to customize the generated JSON schema. The parameters in question are:

  • title
  • description
  • examples
  • json_schema_extra

Read more about JSON schema customization / modification with fields in the [[Customizing JSON Schema]] section of the JSON schema docs.

computed_field 装饰器

pydantic.fields.computed_field

computed_field 装饰器能够将  property or cached_property 装饰的字段也囊括进序列化中

This can be useful for fields that are computed from other fields, or for fields that are expensive to computed (and thus, are cached).

from pydantic import BaseModel, computed_field


class Box(BaseModel):
width: float
height: float
depth: float

@computed_field
def volume(self) -> float:
return self.width * self.height * self.depth


b = Box(width=1, height=2, depth=3)
print(b.model_dump())
#> {'width': 1.0, 'height': 2.0, 'depth': 3.0, 'volume': 6.0}

As with regular fields, computed fields can be marked as being deprecated:

from typing_extensions import deprecated

from pydantic import BaseModel, computed_field


class Box(BaseModel):
width: float
height: float
depth: float

@computed_field
@deprecated("'volume' is deprecated")
def volume(self) -> float:
return self.width * self.height * self.depth